package cn.com.mdx.union.percolation;

import cn.com.mdx.union.UF;

/**
 * @author huangyuan
 * @date 2020/6/12
 * @description 蒙特卡洛模拟
 */
public class Percolation {

    UF grid; //网格
    UF fullGrid; //与top链接的网格
    int top; //虚拟顶点
    int bottom; //虚拟底点
    int gridBound; // 网格边长
    int numOfOpenSite = 0; // 打开的位置个数
    boolean[] siteOpenStatus; //位置打开状态 true:位置打开 false:位置未打开

    public Percolation(int n) {
        grid = new UF(n * n + 2);
        fullGrid = new UF(n * n + 1);
        gridBound = n;
        top = getSiteValue(n, n) + 1;
        bottom = getSiteValue(n, n) + 2;
        siteOpenStatus = new boolean[n * n];
    }

    /**
     * @param row 位置的行值
     * @param col 位置的列值
     * @description 将位置(row, col)打开
     * @date 2020/6/13 6:13 下午
     */
    public void open(int row, int col) {
        //位置值
        int siteValue = getSiteValue(row, col);
        if (!siteOpenStatus[siteValue]) {
            //网格未打开
            //1.打开网格
            siteOpenStatus[siteValue] = true;
            numOfOpenSite++;
            //打开的位置为首行，将位置连接到top
            if (row == 1) {
                grid.union(siteValue, top);
                fullGrid.union(siteValue, top);
            }
            //打开的位置为尾行，将位置连接到bottom
            if (row == gridBound) grid.union(siteValue, bottom);
            //2.将该位置与四周位置贯通
            //上位置
            if (!checkBound(row - 1, col) && siteOpenStatus[getSiteValue(row - 1, col)]) {
                grid.union(siteValue, getSiteValue(row - 1, col));
                fullGrid.union(siteValue, getSiteValue(row - 1, col));
            }
            //下位置
            if (!checkBound(row + 1, col) && siteOpenStatus[getSiteValue(row + 1, col)]) {
                grid.union(siteValue, getSiteValue(row + 1, col));
                fullGrid.union(siteValue, getSiteValue(row + 1, col));
            }
            //左位置
            if (!checkBound(row, col - 1) && siteOpenStatus[getSiteValue(row, col - 1)]) {
                grid.union(siteValue, getSiteValue(row, col - 1));
                fullGrid.union(siteValue, getSiteValue(row, col - 1));
            }
            //右位置
            if (!checkBound(row, col + 1) && siteOpenStatus[getSiteValue(row, col + 1)]) {
                grid.union(siteValue, getSiteValue(row, col + 1));
                fullGrid.union(siteValue, getSiteValue(row, col + 1));
            }

        }
    }

    /**
     * @return int
     * @methodName getNumOfOpenSite
     * @Desc 获取打开位置的个数
     * @Author huangyuan
     * @Date 2020/6/28 11:48 上午
     */
    public int getNumOfOpenSite() {
        return numOfOpenSite;
    }

    /**
     * @param row 位置的行值
     * @param col 位置的列值
     * @methodName isOpen
     * @Desc 查看位置是否打开
     * @Author huangyuan
     * @Date 2020/6/28 11:39 上午
     */
    public boolean isOpen(int row, int col) {
        return siteOpenStatus[getSiteValue(row, col)];
    }

    /**
     * @param row 位置的行值
     * @param col 位置的列值
     * @return true 与顶部连接 false 未与顶部连接
     * @methodName isFull
     * @Desc 获取位置是否与顶部连接
     * @Author huangyuan
     * @Date 2020/6/28 9:35 上午
     */
    public boolean isFull(int row, int col) {
        return fullGrid.connected(top, getSiteValue(row, col));
    }

    /**
     * @return true 贯通 false 未贯通
     * @methodName percolates
     * @Desc 获取渗滤是否贯通
     * @Author huangyuan
     * @Date 2020/6/28 10:33 上午
     */
    public boolean percolates() {
        return grid.connected(top, bottom);
    }

    /**
     * @param row 位置的行值
     * @param col 位置的列值
     * @return 网格值
     * @methodName getSiteValue
     * @Desc 获取位置在网格中的值
     * @Author huangyuan
     * @Date 2020/6/28 9:35 上午
     */
    private int getSiteValue(int row, int col) {
        if (checkBound(row, col)) throw new IllegalArgumentException("传递位置(" + row + ", " + col + "), 不在网格中");
        return (row - 1) * gridBound + col - 1;
    }

    /**
     * @param row 位置横坐标
     * @param col 位置中坐标
     * @return true 超出边界 false 未超出边界
     * @description 判断坐标位置是否在网格内
     * @date 2020/6/13 6:30 下午
     */
    private boolean checkBound(int row, int col) {
        return row <= 0 || row > gridBound || col <= 0 || col > gridBound;
    }

}
